package com.xjm.aspect;

import lombok.extern.slf4j.Slf4j;
import org.myspringframework.core.annotation.Aspect;
import org.myspringframework.core.annotation.Component;
import org.myspringframework.core.annotation.Controller;
import org.myspringframework.core.annotation.Order;
import org.myspringframework.core.aop.aspect.DefaultAspect;

import java.lang.reflect.Method;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * @author jaymin
 * 2021/2/27 0:10
 */
@Aspect(Controller.class)
@Order(0)
@Slf4j
@Component
public class LogExecuteTimeAspect extends DefaultAspect {
    private ThreadLocal<LocalDateTime> startTime = new ThreadLocal<>();

    /**
     * 事前拦截
     *
     * @param targetClass 被代理的目标类
     * @param method      被代理的目标方法
     * @param args        被代理的目标方法对应的参数列表
     * @throws Throwable
     */
    @Override
    public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
        log.info("before {} execute {} method of args [{}].", targetClass.getName(), method.getName(), args);
        startTime.set(LocalDateTime.now());
    }

    /**
     * 事后拦截
     *
     * @param targetClass 被代理的目标类
     * @param method      被代理的目标方法
     * @param args        被代理的目标方法对应的参数列表
     * @param returnValue 被代理的目标方法执行后的返回值
     * @return
     * @throws Throwable
     */
    @Override
    public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
        LocalDateTime endTime = LocalDateTime.now();
        long executeTime = Duration.between(startTime.get(), endTime).toMillis();
        log.info("{} method execute time is {} ms", method.getName(), executeTime);
        return returnValue;
    }

    /**
     * 抛出异常后的拦截
     *
     * @param targetClass 被代理的目标类
     * @param method      被代理的目标方法
     * @param args        被代理的目标方法对应的参数列表
     * @param e           被代理的目标方法执行后抛出的异常
     * @throws Throwable
     */
    @Override
    public void afterThrowing(Class<?> targetClass, Method method, Object[] args, Throwable e) throws Throwable {
        log.error("execute {} method throw exception", method.getName(), e);
        startTime.remove();
        throw new RuntimeException(e);
    }
}
